<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="baidu-site-verification" content="SCzNOI2Wq6" />
    <meta name="renderer" content="webkit">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
    <title>
        daozhuang
    </title>
    
<link rel="stylesheet" href="/libs/highlight/styles/monokai-sublime.css">

    
<link rel="stylesheet" href="/libs/font-awesome/css/font-awesome.min.css">

    
<link rel="stylesheet" href="/css/style.css">

<meta name="generator" content="Hexo 4.2.1"></head>

<body id="bodyx">
    <div class="hd posts">
    <a href="/index.html"><i class="fa fa-reply replay-btn" aria-hidden="true"></i></a>
    <div class="post-title">
        <p>
            Java SPI机制,Spring SPI 机制
        </p>
        <hr>
    </div>
    <div class="post-content">
        <h1 id="Java-spi-机制-Service-Provider-Interface"><a href="#Java-spi-机制-Service-Provider-Interface" class="headerlink" title="Java spi 机制 (Service Provider Interface)"></a>Java spi 机制 (Service Provider Interface)</h1><h3 id="简述"><a href="#简述" class="headerlink" title="简述"></a>简述</h3><p>Service Provider Interface(SPI) 是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文件中，并由服务加载器读取配置文件，加载实现类。这样可以在运行时，动态为接口替换实现类。正因此特性，我们可以很容易的通过 SPI 机制为我们的程序提供拓展功能。</p>
<p>在开发过程中，将问题抽象成API，可以为API提供各种实现。如果现在需要对API提供一种新的实现，我们可以不用修改原来的代码，直接生成新的Jar包，在包里提供API的新实现。通过Java的SPI机制，可以实现了框架的动态扩展，让第三方的实现能像插件一样嵌入到系统中。</p>
<p>Java的SPI类似于springIOC的功能，将装配的控制权移到了程序之外，实现在模块装配的时候不用在程序中动态指明。所以SPI的核心思想就是解耦，这在模块化设计中尤其重要。</p>
<h6 id="简单点就是：spi是Java-内置的服务发现机制，可以被第三方扩展或实现的API，-它可以用来实现框架扩展和可替换的模块。"><a href="#简单点就是：spi是Java-内置的服务发现机制，可以被第三方扩展或实现的API，-它可以用来实现框架扩展和可替换的模块。" class="headerlink" title="简单点就是：spi是Java 内置的服务发现机制，可以被第三方扩展或实现的API， 它可以用来实现框架扩展和可替换的模块。"></a>简单点就是：spi是Java 内置的服务发现机制，可以被第三方扩展或实现的API， 它可以用来实现框架扩展和可替换的模块。</h6><p>服务调用方通过 ServiceLoader.load 加载服务接口的实现类实例<br>服务提供方实现服务接口后， 在自己Jar包的META-INF/services目录下新建一个接口名全名的文件， 并将具体实现类全名写入。</p>
<h3 id="Java-spi-示例"><a href="#Java-spi-示例" class="headerlink" title="Java spi 示例"></a>Java spi 示例</h3><h6 id="项目结构"><a href="#项目结构" class="headerlink" title="项目结构"></a>项目结构</h6><p><img src="https://img-blog.csdnimg.cn/20200715230127912.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl8zNzg2NTMxMg==,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p>
<h6 id="其中-SPI-ServiceImpl-B-和-SPI-ServiceImpl-A-是一样的。"><a href="#其中-SPI-ServiceImpl-B-和-SPI-ServiceImpl-A-是一样的。" class="headerlink" title="其中 SPI-ServiceImpl-B 和 SPI-ServiceImpl-A 是一样的。"></a>其中 SPI-ServiceImpl-B 和 SPI-ServiceImpl-A 是一样的。</h6><p>创建统一的 api 接口</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">SpiInterface</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">seyHello</span><span class="params">(String name)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>创建接口实现</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SpiServiceImplA</span> <span class="keyword">implements</span> <span class="title">SpiInterface</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">seyHello</span><span class="params">(String name)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"AAAAAA"</span>+name);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<pre><code>创建 META-INF 中的文件   注意名称路径，META-INF.services 中的文件是以，接口的全路径命名的。如上图工程中的文件一样。
文件中存放实现类的全路径： com.service.SpiServiceImplA</code></pre><p>编写测试类：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> com.spi.SpiInterface;</span><br><span class="line"><span class="keyword">import</span> com.sun.tools.javac.util.ServiceLoader;</span><br><span class="line"><span class="keyword">import</span> java.util.Iterator;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">application</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">    	<span class="comment">// 把api接口class文件传入方法中，就会自动加载所有实现</span></span><br><span class="line">        ServiceLoader&lt;SpiInterface&gt; load = ServiceLoader.load(SpiInterface<span class="class">.<span class="keyword">class</span>)</span>;</span><br><span class="line">        Iterator&lt;SpiInterface&gt; iterator = load.iterator();</span><br><span class="line">        <span class="keyword">while</span> (iterator.hasNext())&#123;</span><br><span class="line">            SpiInterface spiInterface = iterator.next();</span><br><span class="line">            spiInterface.seyHello(<span class="string">"hello"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h1 id="Spring-SPI-机制"><a href="#Spring-SPI-机制" class="headerlink" title="Spring SPI 机制"></a>Spring SPI 机制</h1><h6 id="对于Spring的SPI机制主要体现在SpringBoot中。体现在springboot四大核心之一的SpringBoot自动装配"><a href="#对于Spring的SPI机制主要体现在SpringBoot中。体现在springboot四大核心之一的SpringBoot自动装配" class="headerlink" title="对于Spring的SPI机制主要体现在SpringBoot中。体现在springboot四大核心之一的SpringBoot自动装配"></a>对于Spring的SPI机制主要体现在SpringBoot中。体现在springboot四大核心之一的SpringBoot自动装配</h6><p>spring 中是使用  SpringFactoriesLoader类加载的：SpringFactoriesLoader.loadFactoryNames<br><img src="https://img-blog.csdnimg.cn/20200715112239783.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl8zNzg2NTMxMg==,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p>
<h6 id="springboot-spi-机制示例："><a href="#springboot-spi-机制示例：" class="headerlink" title="springboot spi 机制示例："></a>springboot spi 机制示例：</h6><p>创建扩展工程<br>pom 文件引入 jar 包</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&lt;dependency&gt;</span><br><span class="line">    &lt;groupId&gt;org.springframework.boot&lt;&#x2F;groupId&gt;</span><br><span class="line">    &lt;artifactId&gt;spring-boot-starter&lt;&#x2F;artifactId&gt;</span><br><span class="line">    &lt;version&gt;2.1.6.RELEASE&lt;&#x2F;version&gt;</span><br><span class="line">    &lt;optional&gt;true&lt;&#x2F;optional&gt;</span><br><span class="line">&lt;&#x2F;dependency&gt;</span><br></pre></td></tr></table></figure>
<p>编写具体实现类</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.springautobean;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: DaoZhuang</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2020/7/15</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ReidsClient</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">seyHello</span><span class="params">()</span></span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"顶顶顶顶"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>编写 configuration 类</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.springautobean;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.Bean;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.Configuration;</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: DaoZhuang</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2020/7/15</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AutoBeanConfiguration</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> ReidsClient <span class="title">reidsClient</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> ReidsClient();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>在 resource 下创建 META-INF/spring.factories 文件</p>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># key 对应springboot定义好的 org.springframework.boot.autoconfigure.EnableAutoConfiguration</span></span><br><span class="line"><span class="comment"># val 对应的是自己编写的 Configuration 配置类</span></span><br><span class="line"><span class="comment"># val 可以是多个，多个最后要加 \ 符号</span></span><br><span class="line"><span class="string">org.springframework.boot.autoconfigure.EnableAutoConfiguration=\</span></span><br><span class="line"><span class="string">com.springautobean.AutoBeanConfiguration</span></span><br></pre></td></tr></table></figure>
<p>新建springboot 项目测试工程，在新建的项目中引入上面编写的项目 在项目中使用容器获取，实例类</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SpringautobeandemoApplication</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        ConfigurableApplicationContext applicationContext = SpringApplication.run(SpringautobeandemoApplication<span class="class">.<span class="keyword">class</span>, <span class="title">args</span>)</span>;</span><br><span class="line">        <span class="comment">// 获取bean </span></span><br><span class="line">        ReidsClient reidsClient = applicationContext.getBean(ReidsClient<span class="class">.<span class="keyword">class</span>)</span>;</span><br><span class="line">        <span class="comment">// 打印 bean对象</span></span><br><span class="line">        System.out.println(reidsClient);</span><br><span class="line">        <span class="comment">// 调用bean 方法</span></span><br><span class="line">        reidsClient.seyHello();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h5 id="整个工程的代码截图："><a href="#整个工程的代码截图：" class="headerlink" title="整个工程的代码截图："></a>整个工程的代码截图：</h5><p><img src="https://img-blog.csdnimg.cn/20200715220615641.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl8zNzg2NTMxMg==,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p>
<h6 id="学习spi机制可以对比，springboot自动装配，dubbo-spi一起学习dubbo-spi"><a href="#学习spi机制可以对比，springboot自动装配，dubbo-spi一起学习dubbo-spi" class="headerlink" title="学习spi机制可以对比，springboot自动装配，dubbo spi一起学习dubbo spi"></a>学习spi机制可以对比，springboot自动装配，dubbo spi一起学习<a href="http://dubbo.apache.org/zh-cn/docs/source_code_guide/dubbo-spi.html" target="_blank" rel="noopener">dubbo spi</a></h6>
    </div>

    
        <hr class="fhr">
        <div id="vcomments"></div>
    
</div>
    <div class="footer" id="footer">
    <p>Copyright © 2020 <a class="flink" href="https://hexo.io" target="_blank" rel="noopener">Hexo</a>-<a class="flink" href="https://github.com/sanjinhub/hexo-theme-geek" target="_blank" rel="noopener">Geek</a>.
        <label class="el-switch el-switch-green el-switch-sm" style="vertical-align: sub;">
            <input type="checkbox" name="switch" id="update_style">
            <span class="el-switch-style"></span>
        </label>
<!--         <script type="text/javascript">
        var cnzz_protocol = (("https:" == document.location.protocol) ? "https://" : "http://");
        document.write(unescape("%3Cspan id='cnzz_stat_icon_1278548644'%3E%3C/span%3E%3Cscript src='" + cnzz_protocol + "v1.cnzz.com/stat.php%3Fid%3D1278548644%26show%3Dpic1' type='text/javascript'%3E%3C/script%3E"));
        </script> -->
    </p>
</div>
<input type="hidden" id="web_style" value="black">
<input type="hidden" id="valine_appid" value="c994h5wXLa8fSMa424BTyVhs-gzGzoHsz">
<input type="hidden" id="valine_appKey" value="4tFbwCi4sYo3yRFgiNFpQd4N">

<script src="/libs/jquery.min.js"></script>


<script src="/libs/highlight/highlight.pack.js"></script>

<script src='//cdn.jsdelivr.net/npm/valine@1.3.10/dist/Valine.min.js'></script>

<script src="/js/js.js"></script>

<style type="text/css">
.v * {
    color: #698fca;
}

.v .vlist .vcard .vhead .vsys {
    color: #3a3e4a;
}

.v .vlist .vcard .vh .vmeta .vat {
    color: #638fd5;
}

.v .vlist .vcard .vhead .vnick {
    color: #6ba1ff;
}

.v a {
    color: #8696b1;
}

.v .vlist .vcard .vhead .vnick:hover {
    color: #669bfc;
}
</style>
</body>

</html>